package com.nvwa.coding.util.num;

import com.fasterxml.jackson.annotation.JsonCreator;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 表示整数范围的实现
 *
 * @author wangjing
 * @since 1.0
 */
public class IntegerRange extends AbstractRange<Integer> {
    private IntegerRange(Bound<Integer> lowerBound, Bound<Integer> upperBound) {
        super(lowerBound, upperBound);
    }

    /**
     * 创建一个包含边界的 {@link IntegerRange}
     *
     * @param from 下限值
     * @param to   上限值
     * @return 由上下限值组成的包含边界的 {@link IntegerRange}
     */
    public static IntegerRange closed(Integer from, Integer to) {
        return new IntegerRange(Bound.inclusive(from), Bound.inclusive(to));
    }

    /**
     * 创建一个不包含边界的 {@link IntegerRange}
     *
     * @param from 下限值
     * @param to   上限值
     * @return 由上下限值组成的不包含边界的 {@link IntegerRange}
     */
    public static IntegerRange open(Integer from, Integer to) {
        return new IntegerRange(Bound.exclusive(from), Bound.exclusive(to));
    }

    /**
     * 创建一个不包含左边界但包含右边界的 {@link IntegerRange}
     *
     * @param from 下限值
     * @param to   上限值
     * @return 由上下限值组成不包含左边界但包含右边界的 {@link IntegerRange}
     */
    public static IntegerRange leftOpen(Integer from, Integer to) {
        return new IntegerRange(Bound.exclusive(from), Bound.inclusive(to));
    }

    /**
     * 创建一个包含左边界但不包含右边界的 {@link IntegerRange}
     *
     * @param from 下限值
     * @param to   上限值
     * @return 由上下限值组成包含左边界但不包含右边界的 {@link IntegerRange}
     */
    public static IntegerRange rightOpen(Integer from, Integer to) {
        return new IntegerRange(Bound.inclusive(from), Bound.exclusive(to));
    }

    /**
     * 使用给定的下限和上限创建一个 {@link IntegerRange}
     *
     * @param lowerBound 下限，不能为 {@code null}
     * @param upperBound 下限，不能为 {@code null}
     * @return 由给定的下限和上限创建的 {@link IntegerRange}
     */
    public static IntegerRange of(Bound<Integer> lowerBound, Bound<Integer> upperBound) {
        return new IntegerRange(lowerBound, upperBound);
    }

    /**
     * 创建一个仅包含指定值的 {@link IntegerRange}
     *
     * @param value {@link IntegerRange} 包含的唯一值
     * @return 一个仅包含指定值的 {@link IntegerRange}
     */
    public static IntegerRange just(Integer value) {
        return IntegerRange.closed(value, value);
    }

    @JsonCreator
    @SuppressWarnings("unchecked")
    public static IntegerRange fromString(String rangeStr) {
        if (rangeStr == null || rangeStr.trim().isEmpty()) {
            return null;
        }

        List<Bound<Integer>> bounds = Arrays.stream(rangeStr.split(","))
                .map(boundStr -> {
                    if ("(-∞".equals(boundStr) || "∞)".equals(boundStr)) {
                        return (Bound<Integer>) Bound.unbounded();
                    }
                    boolean isBounded = true;
                    if (boundStr.startsWith("[")) {
                        boundStr = boundStr.substring(1);
                    } else if (boundStr.startsWith("(")) {
                        isBounded = false;
                        boundStr = boundStr.substring(1);
                    } else if (boundStr.endsWith("]")) {
                        boundStr = boundStr.substring(0, boundStr.length() - 1);
                    } else if (boundStr.endsWith(")")) {
                        isBounded = false;
                        boundStr = boundStr.substring(0, boundStr.length() - 1);
                    } else {
                        return null;
                    }
                    try {
                        Integer value = Integer.parseInt(boundStr);
                        if (isBounded) {
                            return Bound.inclusive(value);
                        }
                        return Bound.exclusive(value);
                    } catch (NumberFormatException e) {
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        if (bounds.size() != 2) {
            throw new IllegalArgumentException("range 格式错误：" + rangeStr);
        }
        return new IntegerRange(bounds.get(0), bounds.get(1));
    }
}
